<!-- Xterm -->
<template>
  <div class="xtermIframeContainer">
    <el-button
      :disabled="loading"
      :loading="loading"
      class="SmallUploadReportBtn"
      @click="onCloseLinux"
    >
      <el-image
        class="SmallUploadReportBtnIcon"
        style="width: 14px; height: 14px"
        :src="require('@/assets/linux/close_icon.png')"
      />
      关机</el-button>
    <div id="terminal" />
  </div>
</template>

<script>
import { Terminal } from 'xterm'
import { FitAddon } from 'xterm-addon-fit'
import 'xterm/css/xterm.css'
import 'xterm/lib/xterm.js'

export default {
  name: '',
  // import引入的组件需要注入到对象中才能使用
  components: {},
  props: {
    loading: {
      type: Boolean,
      required: true
    }
  },
  data() {
    // 这里存放数据
    return {
      term: null,
      cols: 0,
      rows: 0,
      sshPrompt: '',
      command: '',
      sock: null,
      style: {},
      wssh: {},
      // 心跳
      lockReconnect: false, // 是否真正建立连接
      timeout: 28 * 1000, // 30秒一次心跳
      timeoutObj: null, // 心跳心跳倒计时
      serverTimeoutObj: null, // 心跳倒计时
      timeoutnum: null // 断开 重连倒计时

    }
  },
  // 监听属性 类似于data概念
  computed: {},
  // 监控data中的数据变化
  watch: {},
  // 生命周期 - 创建完成（可以访问当前this实例）
  async created() {

  },
  // 生命周期 - 挂载完成（可以访问DOM元素）
  mounted() {
  },
  // 生命周期 - keep-alive
  async activated() {

  },
  beforeDestroy() {
    clearTimeout(this.timeoutObj)
    clearTimeout(this.serverTimeoutObj)
    clearTimeout(this.timeoutnum)
  },
  // 方法集合
  methods: {
    // 初始化终端
    initTerm(ws, resp) {
      this.consoleLoading = true
      const that = this
      var encoding = 'utf-8'
      var decoder = window.TextDecoder ? new window.TextDecoder(encoding) : encoding
      var terminal = document.getElementById('terminal')
      var termOptions = {
        cursorBlink: true,
        theme: {
          background: 'black',
          foreground: 'white'
        }
      }
      var url = ws
      var sock = new window.WebSocket(url)
      // 缓存
      that.sock = sock

      var msg = resp
      var url_opts_data = {}

      if (url_opts_data.fontsize) {
        var fontsize = window.parseInt(url_opts_data.fontsize)
        if (fontsize && fontsize > 0) {
          termOptions.fontSize = fontsize
        }
      }

      var term = new Terminal(termOptions)
      term.fitAddon = new FitAddon()
      term.loadAddon(term.fitAddon)
      that.term = term

      function term_write(text) {
        if (term) {
          term.write(text)
          if (!term.resized) {
            resize_terminal(term)
            term.resized = true
            // term.fitAddon.fit()
          }
        }
      }

      function get_cell_size(term) {
        // console.log(`get_cell_size width = ${term._core._renderService._renderer.dimensions.actualCellWidth}`)
        // console.log(`get_cell_size height = ${term._core._renderService._renderer.dimensions.actualCellHeight}`)
        that.style.width = term._core._renderService._renderer.dimensions.actualCellWidth
        that.style.height = term._core._renderService._renderer.dimensions.actualCellHeight
      }

      function current_geometry(term) {
        if (!that.style.width || !that.style.height) {
          try {
            get_cell_size(term)
          } catch (TypeError) {
            console.log(TypeError)
          }
        }

        const innerWidth = window.innerWidth - 680
        // console.log(`${window.innerWidth} / ${that.style.width}`)
        // console.log(`${window.innerHeight} / ${that.style.height}`)
        var cols = parseInt(innerWidth / that.style.width, 10) - 50
        var rows = parseInt(window.innerHeight / that.style.height, 10) - 7
        return { 'cols': cols, 'rows': rows }
      }

      function resize_terminal(term) {
        // console.log('resize_terminal')
        var geometry = current_geometry(term)
        term.on_resize(geometry.cols, geometry.rows)
      }

      function set_encoding(new_encoding) {
      // for console use
        if (!new_encoding) {
          console.log('An encoding is required')
          return
        }

        if (!window.TextDecoder) {
          decoder = new_encoding
          encoding = decoder
          // console.log('Set encoding to ' + encoding)
        } else {
          try {
            decoder = new window.TextDecoder(new_encoding)
            encoding = decoder.encoding
            // console.log('Set encoding to ' + encoding)
          } catch (RangeError) {
            console.log('Unknown encoding ' + new_encoding)
            return false
          }
        }
      }

      this.wssh.set_encoding = set_encoding

      if (url_opts_data.encoding) {
        if (set_encoding(url_opts_data.encoding) === false) {
          set_encoding(msg.encoding)
        }
      } else {
        set_encoding(msg.encoding)
      }

      this.wssh.geometry = function() {
      // for console use
        var geometry = this.current_geometry(term)
        console.log('Current window geometry: ' + JSON.stringify(geometry))
      }

      this.wssh.send = function(data) {
      // for console use
        if (!sock) {
          console.log('Websocket was already closed')
          return
        }

        if (typeof data !== 'string') {
          console.log('Only string is allowed')
          return
        }

        try {
          JSON.parse(data)
          sock.send(data)
        } catch (SyntaxError) {
          data = data.trim() + '\r'
          sock.send(JSON.stringify({ 'data': data }))
        }
      }

      this.wssh.reset_encoding = function() {
      // for console use
        if (encoding === msg.encoding) {
          console.log('Already reset to ' + msg.encoding)
        } else {
          set_encoding(msg.encoding)
        }
      }

      this.wssh.resize = function(cols, rows) {
      // for console use
        if (term === undefined) {
          console.log('Terminal was already destroryed')
          return
        }

        var valid_args = false

        if (cols > 0 && rows > 0) {
          var geometry = this.current_geometry(term)
          if (cols <= geometry.cols && rows <= geometry.rows) {
            valid_args = true
          }
        }

        if (!valid_args) {
          console.log('Unable to resize terminal to geometry: ' + format_geometry(cols, rows))
        } else {
          term.on_resize(cols, rows)
        }
      }

      function format_geometry(cols, rows) {
        return JSON.stringify({ 'cols': cols, 'rows': rows })
      }

      term.on_resize = function(cols, rows) {
        // console.log('on_resize')
        if (cols !== this.cols || rows !== this.rows) {
          // console.log('Resizing terminal to geometry: ' + format_geometry(cols, rows))
          this.resize(cols, rows)
          sock.send(JSON.stringify({ 'resize': [cols, rows] }))
        }
        // term.FitAddon.fit()
      }

      term.onData(function(data) {
        console.log('term.onData')

        sock.send(JSON.stringify({ 'data': data }))
      })

      sock.onopen = function() {
        console.log('sock.onopen')
        term.open(terminal)
        term.focus()
        if (url_opts_data.command) {
          setTimeout(function() {
            sock.send(JSON.stringify({ 'data': url_opts_data.command + '\r' }))
          }, 500)
        }
        // 开始做心跳
        that.reset()
      }

      sock.onmessage = (msg) => {
        // console.log('sock.onmessage')
        this.read_file_as_text(msg.data, term_write, decoder)
      }

      sock.onerror = function(e) {
        console.log('sock.onerror')
        console.error(e)
        that.$emit('onAbnormalShutdown')
      }

      sock.onclose = function(e) {
        console.log('sock.onclose')
        // 清空sock
        sock = undefined
        that.sock = undefined
        term.dispose()
        term = undefined

        // 清除时间
        clearTimeout(that.timeoutObj)
        clearTimeout(that.serverTimeoutObj)

        // 获取缓存Id
        const linuxData = localStorage.getItem('linuxData') || ''

        // 关闭有两种情况，一是正常逻辑关闭,// 另外一种是 ws 突然断开意外关闭

        if (linuxData !== '') {
          abnormalShutdown()
        }
      }

      // sock.onclose 异常关闭逻辑
      function abnormalShutdown() {
        that.$emit('onAbnormalShutdown')
      }
    },
    read_file_as_text(file, callback, decoder) {
      if (!window.TextDecoder) {
        this.read_as_text_with_encoding(file, callback, decoder)
      } else {
        this.read_as_text_with_decoder(file, callback, decoder)
      }
    },
    read_as_text_with_decoder(file, callback, decoder) {
      var reader = new window.FileReader()

      if (decoder === undefined) {
        decoder = new window.TextDecoder('utf-8', { 'fatal': true })
      }

      reader.onload = function() {
        var text
        try {
          text = decoder.decode(reader.result)
        } catch (TypeError) {
          console.log('Decoding error happened.')
        } finally {
          if (callback) {
            callback(text)
          }
        }
      }

      reader.onerror = function(e) {
        console.error(e)
      }

      reader.readAsArrayBuffer(file)
    },
    read_as_text_with_encoding(file, callback, encoding) {
      var reader = new window.FileReader()

      if (encoding === undefined) {
        encoding = 'utf-8'
      }

      reader.onload = function() {
        if (callback) {
          callback(reader.result)
        }
      }

      reader.onerror = function(e) {
        console.error(e)
      }

      reader.readAsText(file, encoding)
    },
    parse_xterm_style() {
      // var text = $('.xterm-helpers style').text()
      // var arr = text.split('xterm-normal-char{width:')
      // this.style.width = parseFloat(arr[1])
      // arr = text.split('div{height:')
      // this.style.height = parseFloat(arr[1])
    },

    get_cell_size(term) {
      this.style.width = term._core._renderService._renderer.dimensions.actualCellWidth
      this.style.height = term._core._renderService._renderer.dimensions.actualCellHeight
    },
    reset() {
      // console.log('重置心跳')
      var that = this
      // 清除时间
      clearTimeout(that.timeoutObj)
      clearTimeout(that.serverTimeoutObj)
      // 重启心跳
      that.start()
    },
    start() {
      // 开启心跳
      var self = this
      self.timeoutObj && clearTimeout(self.timeoutObj)
      self.serverTimeoutObj && clearTimeout(self.serverTimeoutObj)
      self.timeoutObj = setTimeout(function() {
        // console.log('self.sock.readyState')
        // console.log(self.sock.readyState)
        // 这里发送一个心跳，后端收到后，返回一个心跳消息，
        if (self.sock.readyState === 1) {
          // 如果连接正常
          self.sock.send(JSON.stringify({ 'data': '' }))
          // 自调用
          self.reset()
        }
        self.serverTimeoutObj = setTimeout(function() {
          console.log('超时关闭')
          // 超时关闭
          // self.close()
        }, self.timeout)
      }, self.timeout)
    },
    close() {
      this.sock.close()
      console.log('socket已经关闭')
    },
    onCloseLinux() {
      this.$emit('onCloseLinux')
    }

  },

  beforeRouteEnter(to, from, next) {
    next(vm => {

    })
  },
  // 离开页面前
  beforeRouteLeave(to, from, next) {
    next()
  }
}
</script>
<style lang="scss" scoped>
.xtermIframeContainer {
  width: 100%;
  height: 100%;
  position: relative;
  .SmallUploadReportBtn {
    min-width: 70px;
    height: 26px;
    padding: 0;
    margin: 0;
    line-height: 26px;
    text-align: left;
    padding: 0 9px;
    border: 1px solid #FFFFFF;
    background: #000000;
    @include FifthFont(#FFFFFF,500);
    position: absolute;
    right: 40px;
    top: 21px;
    z-index: 1;
    border-radius: 2px;

    .SmallUploadReportBtnIcon {
      position: relative;
      // margin-right: 4px;
      top: 2px;
    }
  }

  .miniGroup {
    .el-button:hover, .el-button:focus {
      border-color: #EEEEEE;
      background-color: #EEEEEE;
    }
    .SmallUploadReportBtn:nth-child(2) {
      margin-left: 40px;
    }
  }
}

#terminal {
  height: calc(100vh - 120px);
  // height: 100%;
  // width: calc(100vw - 680px);
}
</style>
